package com.onpositive.notes.services;

import java.util.ArrayList;
import java.util.List;

import com.onpositive.collections.BasicCollectionMember;
import com.onpositive.collections.CounterModificator;
import com.onpositive.collections.CounterValueCalculator;
import com.onpositive.collections.Delta;
import com.onpositive.collections.ICollectionDelta;
import com.onpositive.collections.ICollectionMember;
import com.onpositive.collections.IObjectCollection;
import com.onpositive.notes.argumnets.DimensionArgument;
import com.onpositive.notes.argumnets.EntityList;
import com.onpositive.notes.argumnets.EntityListArgument;
import com.onpositive.notes.argumnets.IArgumentsSet;
import com.onpositive.notes.argumnets.MeasuredEntity;
import com.onpositive.notes.argumnets.StringArgument;
import com.onpositive.notes.model.ITemplate;
import com.onpositive.notes.model.ITemplateArgument;
import com.onpositive.units.ParsedDimension;

public class TemplateExecutorRegistry {
	
	public static class BorrowInfExecutor extends GeneralBorrowExecutor{
		public BorrowInfExecutor() {
			super( BasicCollectionRegistry.COLLECTION_NAME_FINANCIAL_RELATIONS_TODO, 1 ) ;
		}		
	}
	
	public static class BorrowPerfExecutor extends GeneralBorrowExecutor{
		public BorrowPerfExecutor() {
			super( BasicCollectionRegistry.COLLECTION_NAME_FINANCIAL_RELATIONS, 1 ) ;
		}		
	}
	public static class GiveBackInfExecutor extends GeneralBorrowExecutor{
		public GiveBackInfExecutor() {
			super( BasicCollectionRegistry.COLLECTION_NAME_FINANCIAL_RELATIONS_TODO, -1 ) ;
		}		
	}
	
	public static class GiveBackPerfExecutor extends GeneralBorrowExecutor{
		public GiveBackPerfExecutor() {
			super( BasicCollectionRegistry.COLLECTION_NAME_FINANCIAL_RELATIONS, -1 ) ;
		}		
	}
	
	public static class MoneyCounterIncrementor extends MoneyCounterModificator{

		public MoneyCounterIncrementor() {
			super( BasicCollectionRegistry.COLLECTION_NAME_MONEY_COUNTER, 1, CounterModificator.INSTRUCTION_CODE_ADD ) ;
		}
	}
	
	public static class MoneyCounterIncrementorTodo extends MoneyCounterModificator{

		public MoneyCounterIncrementorTodo() {
			super( BasicCollectionRegistry.COLLECTION_NAME_MONEY_COUNTER_TODO, 1, CounterModificator.INSTRUCTION_CODE_ADD ) ;
		}
	}
	
	public static class MoneyCounterDecrementor extends MoneyCounterModificator{

		public MoneyCounterDecrementor() {
			super( BasicCollectionRegistry.COLLECTION_NAME_MONEY_COUNTER, -1, CounterModificator.INSTRUCTION_CODE_ADD ) ;
		}
	}
	
	public static class MoneyCounterDecrementorTodo extends MoneyCounterModificator{

		public MoneyCounterDecrementorTodo() {
			super( BasicCollectionRegistry.COLLECTION_NAME_MONEY_COUNTER_TODO, -1, CounterModificator.INSTRUCTION_CODE_ADD ) ;
		}
	}
	
	public static class MoneyCounterSetter extends MoneyCounterModificator{

		public MoneyCounterSetter() {
			super( BasicCollectionRegistry.COLLECTION_NAME_MONEY_COUNTER, 1, CounterModificator.INSTRUCTION_CODE_SET ) ;
		}
	}
	
	public static class GeneralStorageContributor extends StoreModificator{

		public GeneralStorageContributor() {
			super(BasicCollectionRegistry.COLLECTION_NAME_GENERAL_STORAGE,1);
		}		
	}
	
	public static class GeneralStorageContributorTodo extends StoreModificator{

		public GeneralStorageContributorTodo() {
			super(BasicCollectionRegistry.COLLECTION_NAME_GENERAL_STORAGE_TODO,1);
		}		
	}
	
	public static class GeneralStorageRemover extends StoreModificator{

		public GeneralStorageRemover() {
			super(BasicCollectionRegistry.COLLECTION_NAME_GENERAL_STORAGE,-1);
		}		
	}
	
	public static class GeneralStorageRemoverTodo extends StoreModificator{

		public GeneralStorageRemoverTodo() {
			super(BasicCollectionRegistry.COLLECTION_NAME_GENERAL_STORAGE_TODO,-1);
		}		
	}
	
	public abstract static class GeneralBorrowExecutor implements ITemplateExecutor{

		String collectionId ;
		public GeneralBorrowExecutor(String collectionId, int mult) {
			super();
			this.collectionId = collectionId;
			this.mult = mult;
		}

		int mult ;
		
		@Override
		public ICollectionDelta<?> execute(ITemplate template) {
			
			List<IArgumentsSet> argSets = template.getArgSets();
			if( argSets == null || argSets.isEmpty() )
				return null ;
			
			IObjectCollection col = BasicCollectionRegistry.getInstance().
					getCollectionByName( collectionId ) ;
			
			ArrayList<Integer> idList = new ArrayList<Integer>() ;
			ArrayList<CounterModificator> valList = new ArrayList<CounterModificator>() ;
			
			for( IArgumentsSet set : argSets ){
			
				List<ITemplateArgument<?>> args = set.getArgs() ;
				StringArgument strArg = pickArgument(args, StringArgument.class ) ;
				DimensionArgument dimArg = pickArgument(args, DimensionArgument.class ) ;
				if( dimArg == null || strArg == null )
					return null;
				
				String name = strArg.getArgumentValue().trim();
				ICollectionMember<?> member = col.getMemberByName(name);
				if( member == null ){
					member = new BasicCollectionMember<Object>(name) ;
					col.addNewMember(member) ;
				}
				idList.add( member.getId() ) ;
				
				ParsedDimension pd = dimArg.getArgumentValue();
				ParsedDimension val = computeDeltaValue(pd,mult);
				CounterModificator cm = new CounterModificator( val, CounterModificator.INSTRUCTION_CODE_ADD ) ;
				valList.add(cm) ;
			}
			
			if( idList.isEmpty() || valList.isEmpty() )
				return null ;
			
			Delta<CounterModificator> delta = new Delta<CounterModificator>(
					CounterModificator.class,
					template.getId(),
					idList,
					valList );
			
			col.registerDelta(delta) ;
			return delta ;
		}		
	}
	
	private static ParsedDimension computeDeltaValue(ParsedDimension pd, int mult)
	{
		Double lb = pd.getLowerBound();
		Double ub = pd.getUpperBound();
		
		ParsedDimension val ;
		if( lb == null ){
			ub = ub * mult ;
			val = new ParsedDimension(pd, ub, pd.getComment() ) ;
		}
		else{
			if( mult < 0 ){
				double d = ub ;
				ub = mult * lb ;
				lb = mult * d ;
			}
			else{
				ub = ub*mult ;
				lb = lb*mult ;
			}
			val = new ParsedDimension( lb, ub, pd, pd.getComment() ) ;			
		}
		return val ;
	}
	
	public static class MoneyCounterModificator implements ITemplateExecutor{
		
		public MoneyCounterModificator(String collectionId,int mult, int code) {
			super();
			this.mult = mult;
			this.collectionId = collectionId ;
			this.instructionCode = code ;
		}
		
		int mult ;
		String collectionId ;
		int instructionCode ;
		
		@Override
		public ICollectionDelta<?> execute(ITemplate template) {
			
			List<IArgumentsSet> argSets = template.getArgSets();
			if( argSets == null || argSets.isEmpty() )
				return null ;		
			
			ArrayList<ParsedDimension> dimList = new ArrayList<ParsedDimension>() ;			
			for( IArgumentsSet set : argSets ){
				
				List<ITemplateArgument<?>> args = set.getArgs() ;
				DimensionArgument dimArg = pickArgument(args, DimensionArgument.class ) ;
				if( dimArg == null )
					continue ;
				
				ParsedDimension pd = dimArg.getArgumentValue();
				ParsedDimension pDim = computeDeltaValue(pd,mult);
				dimList.add(pDim) ;		
			}			
			if( dimList.isEmpty() )
				return null ;
			
			Double lSum = null, uSum = 0. ;
			for( ParsedDimension pd : dimList ){
				
				if( pd.isRange() ){
					if(lSum == null)
						lSum = 0. ;
					lSum += pd.getLowerBound() ;
				}
				
				uSum += pd.getUpperBound() ;
			}
			ParsedDimension result ;
			ParsedDimension pDim = dimList.iterator().next();
			if( lSum == null ){				
				result = new ParsedDimension( pDim, uSum, pDim.getComment() ) ;				
			}
			else{
				result = new ParsedDimension( lSum, uSum, pDim, pDim.getComment() ) ;
			}
			
			CounterModificator val = new CounterModificator(result, instructionCode) ;
			
			IObjectCollection col = BasicCollectionRegistry.getInstance().
					getCollectionByName( collectionId ) ;
			
			ICollectionMember<?> member = col.getAllMembers().iterator().next() ;
			int memberId = member.getId();
			
			Delta<CounterModificator> delta = new Delta<CounterModificator>(
					CounterModificator.class,
					template.getId(),
					new int[]{ memberId } ,
					new CounterModificator[]{ val } ) ;
			
			col.registerDelta(delta) ;	
			return delta ;
		}
	}
	
	public static class StoreModificator implements ITemplateExecutor{
		
		public StoreModificator(String collectionId,int mult) {
			super();
			this.mult = mult;
			this.collectionId = collectionId;
		}

		int mult ;
		String collectionId ;		
		
		@Override
		public ICollectionDelta<?> execute(ITemplate template) {
			
			//List<ITemplateArgument<?>> args = template.getArguments();
			List<IArgumentsSet> argSets = template.getArgSets();
			if( argSets == null || argSets.isEmpty() )
				return null ;
			
			IObjectCollection col = BasicCollectionRegistry.getInstance().
					getCollectionByName( collectionId ) ;
			
			ArrayList<Integer> idList = new ArrayList<Integer>() ;
			ArrayList<ArrayList> valList = new ArrayList<ArrayList>() ;
			for( IArgumentsSet argSet : argSets  ){
				
				ArrayList<ParsedDimension> dimList = new ArrayList<ParsedDimension>() ;				
				List<ITemplateArgument<?>> args = argSet.getArgs() ;
				
				StringArgument strArg = pickArgument(args, StringArgument.class ) ;
				for( ITemplateArgument<?> arg : args ){
					if(!(arg instanceof DimensionArgument))
						continue ;
					
					DimensionArgument dArg = (DimensionArgument) arg ;
					ParsedDimension pDim = dArg.getArgumentValue();
					dimList.add(pDim) ;
				}
				
				String name = strArg.getArgumentValue().trim() ;
				ICollectionMember<?> member = col.getMemberByName(name);
				if( member == null ){
					member = new BasicCollectionMember<Object>(name) ;
					col.addNewMember(member) ;
				}				
				idList.add( member.getId() ) ;
				valList.add(dimList) ;
			}
			
			if( idList.isEmpty() || valList.isEmpty() )
				return null ;
			
			Delta<ArrayList> delta = new Delta<ArrayList>(
					ArrayList.class,
					template.getId(),
					idList,
					valList ) ;
			
			col.registerDelta(delta) ;
			return delta ;
		}
		
	}
	
	@SuppressWarnings("unchecked")
	static <T>T pickArgument( List<?> list, Class<T> clazz){
		
		for( Object obj: list ){
			if( clazz.isInstance(obj) )
				return (T) obj ;
		}
		return null ;
	}

}
